/*
 *  ApplicationEngine.cpp
 *
 *  Created by BongjuKim on 11. 1. 30..
 *  Copyright 2011 thinkware. All rights reserved.
 *
 */

#include "Interfaces.hpp"
#include <iostream>


using namespace std;

static const int SurfaceCount = 6;
static const int ButtonCount = SurfaceCount - 1;

const string TextureFiles[] = {
	"nehe.bmp",
	"crate.bmp",
	"glass.bmp",
};

const string TextFiles[] = {
	"f15.data",
};

class ApplicationEngine : public IApplicationEngine 
{
public:
	ApplicationEngine(IRenderingEngine* renderingEngine);
	~ApplicationEngine();
	void Initialize(int width, int height);
	void OnFingerUp(ivec2 location);
	void OnFingerDown(ivec2 location);
	void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
	void Render(float magValue, float accelValue) const;
	void UpdateAnimation(float dt);
	
private:
    void PopulateVisuals(Visual* visuals) const;
	int  MapToButton(ivec2 touchpoint) const;
    vec3 MapToSphere(ivec2 touchpoint) const;
	void LoadTexture();
	void LoadText();
	
	float m_trackballRadius;
	ivec2 m_screenSize;
	ivec2 m_centerPoint;
	ivec2 m_fingerStart;
    bool m_spinning;
    Quaternion m_orientation;
    Quaternion m_previousOrientation;
    ivec2 m_buttonSize;
	
	IRenderingEngine* m_renderingEngine;
	int m_textureIndex;
	int m_textIndex;
	float m_timer;
	
//	vec3 accelValues;
};


IApplicationEngine* CreateApplicationEngine(IRenderingEngine* renderingEngine)
{
	return new ApplicationEngine(renderingEngine);
}


ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine) : 
		m_renderingEngine(renderingEngine),
		m_timer(0)
{
}

ApplicationEngine::~ApplicationEngine()
{
	delete m_renderingEngine;
}

void ApplicationEngine::Initialize(int width, int height)
{
	m_trackballRadius = width / 3;
    m_buttonSize.y = height / 10;
    m_buttonSize.x = 4 * m_buttonSize.y / 3;
	m_screenSize = ivec2(width, height);
	m_centerPoint = m_screenSize / 2;
//    m_screenSize = ivec2(width, height - m_buttonSize.y);


	
	m_renderingEngine->Initialize(width, height);

//	m_textureIndex = 2;
//	LoadTexture();
	
	m_textIndex = 0;
	LoadText();
}

void ApplicationEngine::PopulateVisuals(Visual* visuals) const
{
//    visuals[m_currentSurface].Color = m_spinning ? vec3(1, 1, 0.75f) : vec3(1, 1, 0.5f);
//    visuals[m_currentSurface].LowerLeft = ivec2(0, m_buttonSize.y);
//    visuals[m_currentSurface].ViewportSize = ivec2(m_screenSize.x, m_screenSize.y);
//    visuals[m_currentSurface].Orientation = m_orientation;
}

#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)


void ApplicationEngine::Render(float magValue, float accelValue) const
{
	std::cout << "magValue:" << magValue << "\r\n";
	std::cout << "accelValue:" << accelValue << "\r\n";
	
	Visual visual;
	visual.Color = m_spinning ? vec3(1, 1, 1) : vec3(0, 1, 1);
	visual.LowerLeft = ivec2(0, 48);
	visual.ViewportSize = ivec2(320, 480);
	visual.Orientation = m_orientation;
	
	if (m_spinning == false) {
		
		// Portrait mode에서 yaw를 값을 구하기 위하여 Y축을 중심으로 회전을 한다.
		Quaternion roll = Quaternion::CreateFromAxisAngle(vec3(1.0f, 0.0f, 0.0f), DEGREES_TO_RADIANS(magValue));
		// Portrait mode에서 pitch는 X축 중심으로 회전을 한다.
		Quaternion pitch = Quaternion::CreateFromAxisAngle(vec3(0.0f, 0.0f, 1.0f), DEGREES_TO_RADIANS(accelValue));
		Quaternion compensation = Quaternion::CreateFromAxisAngle(vec3(0.0f, 0.0f, 1.0f), DEGREES_TO_RADIANS(90));
		Quaternion orientation = compensation.Rotated(roll);
		orientation = orientation.Rotated(pitch);
		//roll.Rotated(pitch);
		//orientation = orientation.Rotated(compensation);
		
		visual.Orientation = orientation;
	}

	
	
	
	
	m_renderingEngine->Render(&visual);
	
//	m_renderingEngine->Render();
}


void ApplicationEngine::UpdateAnimation(float dt)
{
//    m_timer += dt;
//    if (m_timer > 0.75f) {
//        m_timer = 0;
//        OnFingerDown(ivec2(0, 0));
//    }
}

void ApplicationEngine::OnFingerDown(ivec2 location)
{
    m_spinning = true;
    m_fingerStart = location;
    m_previousOrientation = m_orientation;
	
//	m_renderingEngine->OnFingerDown(location);
}

void ApplicationEngine::OnFingerUp(ivec2 location)
{
	m_spinning = false;
	
//	m_renderingEngine->OnFingerUp(location);
}

void ApplicationEngine::OnFingerMove(ivec2 oldLocation, ivec2 location)
{
	if (m_spinning) {
		vec3 start = MapToSphere(m_fingerStart);
		vec3 end = MapToSphere(location);
		Quaternion delta = Quaternion::CreateFromVectors(start, end);
		m_orientation = delta.Rotated(m_previousOrientation);
	}

//	m_renderingEngine->OnFingerMoved(oldLocation, location);
}


void ApplicationEngine::LoadTexture()
{
    string filename = TextureFiles[m_textureIndex];
    m_renderingEngine->SetBmpTexture(filename);
}

void ApplicationEngine::LoadText()
{
	string filename = TextFiles[m_textIndex];
	m_renderingEngine->SetTextData(filename);
}

// For Sensor
//void ApplicationEngine::SetAccel(float ax, float ay, float az)
//{
//	m_renderingEngine->SetAccel(ax, ay, az);
//	
//
//	if (m_spinning == false) {
//
//		vec3 start;
//		vec3 end;
//		
//		ivec2 newFrame;
//		ivec2 origin;
//		
//		newFrame.x = m_screenSize.x * (0.5+(az+1.0)/2);
//		newFrame.y = m_screenSize.y * (0.5+(az+1.0)/2);
//		
//		origin.x = m_screenSize.x * (1.0-(ax+1.0)/2) - (newFrame.x/2);
//		origin.y = m_screenSize.y * (1.0-(ay+1.0)/2) - (newFrame.y/2);
//		
//		start = MapToSphere(m_fingerStart);
//		end = MapToSphere(origin);
//		
//		Quaternion delta = Quaternion::CreateFromVectors(start, end);
//		m_orientation = delta.Rotated(m_previousOrientation);
//	} else {
////		m_previousOrientation = m_orientation;
////		fingerStart = m_fingerStart;
//	}
//
//
//	accelValues.x = ax;
//	accelValues.y = ay;
//	accelValues.z = az;
//}
// For Sensor


vec3 ApplicationEngine::MapToSphere(ivec2 touchpoint) const
{
    vec2 p = touchpoint - m_centerPoint;
    
    // Flip the Y axis because pixel coords increase towards the bottom.
    p.y = -p.y;
    
    const float radius = m_trackballRadius;
    const float safeRadius = radius - 1;
    
    if (p.Length() > safeRadius) {
        float theta = atan2(p.y, p.x);
        p.x = safeRadius * cos(theta);
        p.y = safeRadius * sin(theta);
    }
    
    float z = sqrt(radius * radius - p.LengthSquared());
    vec3 mapped = vec3(p.x, p.y, z);
    return mapped / radius;
}

int ApplicationEngine::MapToButton(ivec2 touchpoint) const
{
    if (touchpoint.y  < m_screenSize.y - m_buttonSize.y)
        return -1;
    
    int buttonIndex = touchpoint.x / m_buttonSize.x;
    if (buttonIndex >= ButtonCount)
        return -1;
    
    return buttonIndex;
}

